import sys
import os
import unittest
import inspect
import figleaf
import figleaf.annotate_html
import shutil
import exceptions
import sqlite3

class Tester(unittest.TestCase):

    org_modules = set(sys.modules.copy().keys())

    @classmethod
    def reporter_start(cls, use_figleaf = True):
        if use_figleaf:
            cls.figleaf_dir = inspect.getabsfile(sys.modules[__name__]).split('.')[-2].split(os.sep)[-1]
            if os.path.isdir(cls.figleaf_dir):
                shutil.rmtree(cls.figleaf_dir, ignore_errors = True)
            os.mkdir(cls.figleaf_dir)
            figleaf.start()
        
        cls.figleaf_use = use_figleaf
        
    @classmethod
    def reporter_stop(cls, viewer = None):
        if cls.figleaf_use:
            figleaf.stop()
            figleaf.annotate_html.report_as_html(figleaf.get_info(), directory = cls.figleaf_dir, exclude_patterns = [], files_list = {})
            print "\n\nWrote figleaf report to '" + cls.figleaf_dir + "' . . . "
            
        import pod
        print "\n\nUsing pod module:\n\t'" + inspect.getabsfile(pod) +"'"

    @classmethod
    def run_test(cls, test):
        unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(test))

    def __le__(self, item):
        if 'first_equals_item' in self.__dict__:
            self.assertEqual(self.first_equals_item, item)
            del self.__dict__['first_equals_item']
        else:
            self.first_equals_item = item
    
    def __eq__(self, item):
        if not isinstance(item, (list, tuple)):
            item = tuple(item)
        self.assertEqual(item[0], item[1])
    
    def setUp(self):
        self.filename = 'suite_0.sqlite3.db'

    def clear_mod_imports(self):
        new_modules = set(sys.modules.copy().keys()) - Tester.org_modules
        for key in new_modules:
            del sys.modules[key]

    def tearDown(self):
        self.clear_mod_imports()
        if os.path.isfile(self.filename):
            #os.remove(self.filename)
            pass
 
    class raises(object):
    
        def __init__(self, *args):
            self.exceptions = tuple(args)
            
        def __call__(self, fn):

            excepted = False
            
            try:
                fn()
            except self.exceptions:
                excepted = True
            
            if excepted is False:
                raise exceptions.AssertionError, "Exceptions not raised: " + str(self.exceptions)
                
""" THE BASICS """            
class Basics(Tester):
  
    def test_basics(self):
        self.basic_section_0()
        self.clear_mod_imports()
        self.basic_section_1()        
        self.clear_mod_imports()
        
    def basic_section_0(self):
        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            pass
        
        class Punk(Person):
            pass
        
        class Email(pod.Object):
            ctime = pod.typed.TimeCreate()
        
        email_0 = Email(title = 'FYI',      age = 1)
        email_1 = Email(title = 'GO HOME',  age = 2)
        email_2 = Email(title = 'HI',       age = 3.3)
        
        Person(name = 'Fred',  age = 32,     emails = [email_0, email_1])
        Person(name = 'Hsing', age = 35.3,   emails = [])
        Person(name = 'Joan',  age = 22.3,   emails = [email_2])
        
        Punk(name = 'Mred',  age = 12,     emails = [email_0, email_1])
        Punk(name = 'Xsing', age = 15.3,   emails = [email_0, email_1, email_2])
        Punk(name = 'Yoan',  age = 12.3,   emails = [email_2])

        self <= [peep.name for peep in Person.where.age == 32]
        self <= ['Fred']

        self <= [peep.name for peep in Person.where.name[1:3] == 're']
        self <= ['Fred', 'Mred']

        self <= [peep.name for peep in (Person.where.name[-3:-1] == 're') | (Person.where.age == 35.3)]
        self <= ['Fred', 'Hsing', 'Mred']

        self <= [peep.name for peep in Person.where.emails == [email_2]]
        self <= ['Joan', 'Yoan']

        self <= [peep.name for peep in Person.where.age < 25]
        self <= ['Joan', 'Mred', 'Xsing', 'Yoan']

        self <= [peep.name for peep in (Person.where.emails == [email_2]) & (Person.where.age < 25)]
        self <= ['Joan', 'Yoan']
        
        self <= [peep.name for peep in (Person.where.emails == [email_2]) & (Person.where.age < 10)]
        self <= []
        
        self <= Email.get_count()
        self <= 3

        self <= Person.get_count()
        self <= 6

        self <= Person.get_count(child_classes = False)
        self <= 3

        self <= Punk.get_count(child_classes = False)
        self <= 3

        self <= [email.title for email in Email.ctime.younger_than(days = 1)]
        self <= ['FYI', 'GO HOME', 'HI']

        import time
        time.sleep(1)
        
        self <= [email.title for email in Email.ctime.older_than(seconds = 1)]
        self <= ['FYI', 'GO HOME', 'HI']

        db.commit(close = True)
        
    def basic_section_1(self):
        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            name   = pod.typed.String()
            age    = pod.typed.Int(index = True)
            alive  = pod.typed.Boolean()
            boss   = pod.typed.PodObject()
            emails = pod.typed.Object() 
        
        class Punk(Person):
            weight = pod.typed.Float()
        
        class Email(pod.Object):
            ctime = pod.typed.TimeCreate()
        
        email_0 = Email(title = 'FYI',      age = 1)
        email_1 = Email(title = 'GO HOME',  age = 2)
        email_2 = Email(title = 'HI',       age = 3.3)
        
        boss = Person(name = 'Don', age = 55, emails = [email_0, email_1], random = 'FOO')
        Person(name = 'Fred',  age = 32,      emails = [email_0, email_1], boss = boss, random = 'FOO')
        Person(name = 'Hsing', age = 35.3,    emails = [], boss = boss, random = 10.0)
        Person(name = 'Joan',  age = 22.3,    emails = [email_2], boss = boss, random = 20.0)
        
        Punk(name = 'Mred',  age = 12,     weight = 30.1, emails = [email_0, email_1], boss = boss, random = 30.0)
        Punk(name = 'Xsing', age = 15.3,   weight = 40.1, emails = [email_0, email_1, email_2], boss = boss, random = 10.0)
        Punk(name = 'Yoan',  age = 12.3,   weight = 50.1, emails = [email_2], boss = None, random = 11.3, some_attr = [10, 20, 30])

        db.commit(clear_cache = False)
        
        self <= [peep.name for peep in (Person.age > 10) & (Person.where.random < 24.0)]
        self <= ['Hsing', 'Joan', 'Xsing', 'Yoan']

        self <= [peep.name for peep in (Person.age > 10) | (Person.where.random < 24.0)]
        self <= ['Don', 'Fred', 'Hsing', 'Joan', 'Mred', 'Xsing', 'Yoan']

        self <= [peep.name for peep in ((Person.age > 90) & (Person.where.random < 12.0)) | (Person.boss == boss)]
        self <= ['Fred', 'Hsing', 'Joan', 'Mred', 'Xsing']

        self <= [peep.name for peep in (Person.alive == None) & (Person.where.random == 'FOO')]
        self <= ['Don', 'Fred']
        
        for peep in Person.name[1:4] == 'red':
            peep.alive = True
        
        self <= [(peep.alive, peep.name) for peep in (Person.alive == True)]
        self <= [(True, 'Fred'), (True, 'Mred')]
        
        self <= [(peep.alive, peep.random) for peep in (Person.alive == True) & (Person.where.random > 25)]
        self <= [(True, 'FOO'), (True, 30.0)]

        self <= [(peep.alive, peep.name, peep.random) for peep in (Person.alive == True) & (Person.where.random > 25)  | (Person.where.some_attr)]
        self <= [(True, 'Fred', 'FOO'), (True, 'Mred', 30.0), (None, 'Yoan', 11.300000000000001)]

        self <= [(peep.name, peep.age) for peep in Person.age == 32]
        self <= [('Fred', 32)]

        self <= [peep.name for peep in Person.name[1:3] == 're']
        self <= ['Fred', 'Mred']

        self <= [(peep.name, peep.age) for peep in (Person.name[-3:-1] == 're') | (Person.age == 35)]  # We made this int, so type convert
        self <= [('Fred', 32), ('Hsing', 35.299999999999997), ('Mred', 12)]
        
        self <= [(peep.name,len(peep.emails)) for peep in Person.emails == [email_2]]
        self <= [('Joan', 1), ('Yoan', 1)]

        self <= [(peep.name, peep.age) for peep in Person.age < 25]
        self <= [('Joan', 22.300000000000001), ('Mred', 12), ('Xsing', 15.300000000000001), ('Yoan', 12.300000000000001)]

        self <= [(peep.name,len(peep.emails),peep.age) for peep in (Person.emails == [email_2]) & (Person.age < 25)]
        self <= [('Joan', 1, 22.300000000000001), ('Yoan', 1, 12.300000000000001)]
        
        self <= [peep.name for peep in (Person.emails == [email_2]) & (Person.age < 10)]
        self <= []
        
        self <= Email.get_count()
        self <= 3

        self <= Person.get_count()
        self <= 7

        self <= Person.get_count(child_classes = False)
        self <= 4

        self <= Punk.get_count(child_classes = False)
        self <= 3

        self <= [email.title for email in Email.ctime.younger_than(days = 1)]
        self <= ['FYI', 'GO HOME', 'HI']

        import time
        time.sleep(1)
        
        self <= [email.title for email in Email.ctime.older_than(seconds = 1)]
        self <= ['FYI', 'GO HOME', 'HI']

        db.commit(close = True)
 
    """ QUICK EXAMPLE """     
    def test_quick_example_db(self):
        self.quick_example(clear_cache = True)
        
    def test_quick_example_cache(self):
        self.quick_example(clear_cache = False)

    def quick_example(self, clear_cache):
        import pod

        db = pod.Db(file = self.filename, remove = True)  # First, make a connection to the database . . . 
        
        class Person(pod.Object):  # Now, any object that descends from pod.Object will be stored to last opened database connection. 
             
            def __init__(self, name, age, best_friend = None, **kwargs):
                pod.Object.__init__(self, **kwargs)
                self.name    = name
                self.age     = age
                self.friends = set()   # An attribute can be of any type, including collections
                if best_friend:
                    self.add_friend(friend = best_friend, best = True)
             
            def add_friend(self, friend, best = False):
                self.friends.add(friend)
                friend.friends.add(self)
                if best:
                    self.best_friend   = friend  # A member can even be another pod object. 
                    friend.best_friend = self
        
        # Now, add a class Tycoon that descends from Person . . . 
        class Tycoon(Person):
             
            # Below, we add columns in order to allow faster raw SQL queries.  
            # You don't need to do this -- it just makes querying on that attribute faster. 
            # Add an SQL index for even faster querying at expense of slower insertion into db. 
            age          = pod.typed.Int(index = False)
            catch_phrase = pod.typed.String(index = True)
            yachts       = pod.typed.Object(index = True)
        
        # Let's add another class that descends directly from pod.Object . . .
        class Yacht(pod.Object):
            owner        = pod.typed.Object(index = True)
        
        # Now, add some Person, Tycoon, and Yacht objects to the database . . . 
        fred   = Person(name = 'Fred',   age = 20, some_random_attr = 'foo')
        frank  = Person(name = 'Frank',  age = 80, some_other_random_attr = ['bar', 'baz'])
        don    = Tycoon(name = 'Don',    age = 63, best_friend = fred,  catch_phrase = "You're fired!")
        george = Tycoon(name = 'George', age = 61, best_friend = frank, catch_phrase = "I guarantee it") 
        george.yachts = [Yacht(owner = george, name = 'The One That Got Away'), Yacht(owner = george, name = 'The Trickle Downer')] 
        
        # Now, commit this transaction. You can also call db.rollback() to cancel this transaction. 
        db.commit(clear_cache = clear_cache)  
        
        # Now, get some objects out of the db.        
        if clear_cache:
            # Since the comparison is based upon the Python "id", this should fail as the query will return new objects.
            self.assertNotEqual(set((fred, frank, don, george)), set(peep for peep in Person))
            self.assertEqual(set((o.name, o.age) for o in (fred, frank, don, george)), set((peep.name, peep.age) for peep in Person))
            self.assertNotEqual(set((fred, frank)), set(peep for peep in Person if peep.name[0] == 'F'))
            self.assertEqual(set((o.name, o.age) for o in (fred, frank)), set((peep.name, peep.age) for peep in Person if peep.name[0] == 'F'))
        else:
            # Since the comparison is based upon the Python "id", this should succeed as the query will return the same objects.
            self.assertEqual(set((fred, frank, don, george)), set(peep for peep in Person))
            self.assertEqual(set((fred, frank)), set(peep for peep in Person if peep.name[0] == 'F'))
        
        # Or, if we added a column to the class header, we can query much more efficiently using sqlite3. 
        for tyke in Tycoon.catch_phrase == "You're fired!": 
            self.assertEqual(tyke.name, 'Don')
            self.assertEqual(tyke.age, 63)
            self.assertEqual(tyke.best_friend.name, 'Fred')
        
        # You can even query on pod objects . . .
        for exp, yacht in zip(george.yachts, Yacht.owner == george):
            self.assertEqual(yacht.owner.name, exp.owner.name)
            self.assertEqual(yacht.name, exp.name)
                
        # Or, for full SQL control, use a query object on any defined columns: 
        query = pod.Query(select = Tycoon.age, 
                          where = (Tycoon.age < 62) | Tycoon.catch_phrase.startswith("Yo"),      
                          order_by = Tycoon.age.desc(), 
                          limit = 2)
        
        if clear_cache:
            query_results = [o for o in query]
            self.assertNotEqual([don, george], query_results)
            expect = [(o.name, o.age, o.catch_phrase, o.best_friend.name) for o in (don, george)]
            actual = [(o.name, o.age, o.catch_phrase, o.best_friend.name) for o in query_results]
            self.assertEqual(expect, actual)
        else:
            self.assertEqual([don, george], [o for o in query])
              
        # Just like regular Python objects, you can add attributes on the fly . . . 
        don.another_random_attr = ['foo', 'bar', 'baz']
        
        # Now, commit the 'another_random_attr' change . . . 
        db.commit(clear_cache = clear_cache)
        
        query = pod.Query(select = Tycoon.age, where = Tycoon.age == 63)

        if clear_cache:
            query_results = [o for o in query]
            self.assertEqual(len(query_results), 1)
            q = query_results[0]
            self.assertNotEqual(don, q)
            expect = [don.name, don.age, don.catch_phrase, don.best_friend.name, don.another_random_attr]
            actual = [q.name, q.age, q.catch_phrase, q.best_friend.name, q.another_random_attr]
            self.assertEqual(expect, actual)
        else:
            self.assertEqual([don], [o for o in query])
                
        # Finally, all Db instances and all pod.Object classes contain a 'store' attribute which can be used exactly like a shelve.  Each 'store' is separate from all others . . .  
        Tycoon.store.main_tycoon  = george 
        
        db.commit(clear_cache = clear_cache)
        
        # The store is useful for storing pointers to objects you want access to later on
        for yacht in Yacht.owner == Tycoon.store.main_tycoon: 
            self.assertEqual(yacht.owner.name, 'George')
  
        db.commit(clear_cache = clear_cache, close = True)

    """ FRONT PAGE EXAMPLE """       
    def test_front_page_db(self):
        self.front_page(clear_cache = True)

    def test_front_page_cache(self):
        self.front_page(clear_cache = False)

    def front_page(self, clear_cache):
        import pod
        import models_front
        db = pod.Db(file = self.filename, remove = True, attach = [models_front, pod.set])
        
        hsing  = models_front.Person(name = 'Hsing',    some_attr = 'foo')                    # Now, add some Person, Tycoon, and WorkerBee objects to 
        helen  = models_front.Person(name = 'Helen',    some_other_attr = ['bar', 'baz'])     # the database . . . 
    
        don    = models_front.Tycoon(name = 'Don',      age = 63, catch_phrase = "You're fired!")
        george = models_front.Tycoon(name = 'George',   age = 61, catch_phrase = "I guarantee it") 
        
        bruce  = models_front.WorkerBee(name = 'Bruce', age = 40, boss = don, random_attr = 10.23)
        azhar  = models_front.WorkerBee(name = 'Azhar', age = 40, boss = george, random_attr = {'key': ['value', (1, 2, 3)]})
        
        self.assertEqual(set([peep.name for peep in models_front.Person]),    set(['Hsing', 'Don', 'Bruce', 'Azhar', 'Helen', 'George']))
        self.assertEqual(set([peep.name for peep in models_front.Tycoon]),    set(['Don', 'George']))
        self.assertEqual(set([peep.name for peep in models_front.WorkerBee]), set(['Bruce', 'Azhar']))
        
        db.commit(clear_cache = clear_cache, close = False)  
        
        class Yacht(pod.Object):                                             
            name         = pod.typed.String(index = False)                      
            length       = pod.typed.Float(index = True)                        
            awards       = pod.typed.Object(index = True)                         
            owner        = pod.typed.Object(index = True)                       
                                                                              
            def __init__(self, owner, **kwargs):
                pod.Object.__init__(self, **kwargs)
                self.owner = owner
                self.owner.yachts.append(self)
                self.photos = []                                                 
             
        db.attach(Yacht)
             
        Yacht(owner = george, name = 'The Trickle Downer', length = 40.5)
        Yacht(owner = george, name = 'The TARP Runner',    length = 42.1, some_random_attr = 'foo')
        Yacht(owner = george, name = 'Mr. Pickles',        length = 45.1, some_random_attr = 'foo')
    
        self.assertEqual(set([yacht.owner.name for yacht in Yacht.name == 'The Trickle Downer']), set(['George', 'George']))                                                                                                       

        self.assertEqual(set([yacht.owner.name for yacht in Yacht.owner == george]), set(['George', 'George']))                                                                                                       
    
        query = pod.Query(select = Yacht.name | Yacht.length,                    # Or, for full SQL control, use a query object on any defined 
                          where = (Yacht.length < 41) | (Yacht.length == 42.1),  # typed.  Conditionals are chained together with '|' or '&'. 
                          order_by = Yacht.length.desc(), 
                          limit = 2)
        
        self.assertEqual([yacht.length for yacht in query], [42.100000000000001, 40.5])
        
    
        db.commit(close = True)

""" THE EXTENED TEST """            
class Extended(Tester):
  
    def test_mutables(self):

        self.mutants_0()
        self.clear_mod_imports()
        self.mutants_1()
        self.clear_mod_imports()
    
    def mutants_0(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            pass
        
        class Place(pod.Object):
            pass
        
        peep  = Person()
        place = Place()
        
        peep.mutant  = [1, 2, 3]
        place.mutant = peep.mutant
        place.name   = 'Fred'
        
        peep.mutant[2] = 'HI'
        
        db.commit(close = True)

    def mutants_1(self):

        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            pass
        
        class Place(pod.Object):
            pass
        
        place = (Place.where.name == 'Fred').get_one()
        
        self <= place.mutant[2]
        self <= 'HI'
        
        db.commit(close = True)

    def test_features(self):

        self.feature_is_in()
        self.clear_mod_imports()
    
    def feature_is_in(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            age = pod.typed.Int(index = True)
                
        class Punk(Person):
            pass
        
        class Poet(Person):
            pass
        
        peep  = Person(name = 'Fred', age = 20)
        peep  = Punk(name = 'Tony', age = 30)
        peep  = Poet(name = 'Erza', age = 21)

        self <= set([peep.age for peep in Person.age.is_in([20, 30])])
        self <= set([20, 30])
        
        self <= set([peep.name for peep in Person.where.name.is_in(['Fred'])])
        self <= set(['Fred'])

        self <= set([peep.name for peep in Person.age.is_in([20, 30]) | Person.where.name.is_in(['Fred'])])
        self <= set(['Tony', 'Fred'])
        
        self <= set([peep.name for peep in Person.age.is_in([20, 30]) & Person.where.name.is_in(['Fred'])])
        self <= set(['Fred'])
        
        db.commit(close = True)

""" NoSave Undefined Deleted """            
class NoSaveUndefinedDeleted(Tester):
    
    def test_nosave_undefined_deleted(self):
        self.nsud_0()
        self.clear_mod_imports()
        self.nsud_undefined_error()
        self.clear_mod_imports()
        self.nsud_check_if_exists_0()
        self.clear_mod_imports()
        self.nsud_check_if_exists_1()
        self.clear_mod_imports()        
        self.nsud_undefined_no_error()
        self.clear_mod_imports()
        self.nsud_delete_attr()
        self.clear_mod_imports()
        self.nsud_deleted_attr_access()
        self.clear_mod_imports()
    
    def nsud_0(self):

        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            age      = pod.typed.Int()
            is_alive = pod.typed.Object() 
            
        class Skater(Person):
            weight = pod.typed.Float()

        class Email(pod.Object):
            title = pod.typed.String()
        
        class Temp(pod.NoSave):
            
            def __init__(self, doit):
                self.doit = doit
        
        e0 = Email(title = 'HI')
        e1 = Email(title = 'BYE')
            
        t0 = Temp(doit = True)
        t1 = Temp(doit = False)
            
        self <= t0.get_full_id()
        self <= "0:0"
            
        p0 = Person(age = 32, weight = 204, random = '10', is_alive = True, attr = False, t0 = t0, e0 = e0, e1 = e1, es = [e0, e1])
        p1 = Person(age = 16, weight = 134, random = '5',  is_alive = False, attr = None, t1 = t1, e0 = e0, p0 = p0)

        s0 = Skater(age = 10, weight = 204, random = '10', is_alive = None, attr = True, p0 = p0, p1 = p1, e0 = e0, e1 = e1, es = [e0, e1])
        s1 = Skater(age = 20, weight = 134, random = '5',  is_alive = False, attr = None, p0 = p0, p1 = p1)
        
        db.commit()

        self <= p0.t0.__class__
        self <= Temp

        self <= [peep.age for peep in (Person.is_alive == True) | (Person.where.is_alive == False)]
        self <= [32, 16, 20]

        self <= [peep.age for peep in (Person.where.attr == None) | (Person.where.attr == True)]
        self <= [16, 10, 20]
        
        db.commit(close = True)
  
    def nsud_undefined_error(self):

        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            age      = pod.typed.Int()
            is_alive = pod.typed.Object() 
            
        self <= len([peep for peep in Person]) 
        self <= 2

        p0 = [peep for peep in Person][0]
        p1 = [peep for peep in Person][1]

        self <= p1.p0
        self <= p0

        self <= p0.e0.get_full_id()
        self <= p1.e0.get_full_id()

        self <= p0.e0
        self <= p1.e0
     
        # Make sure Undefined instances are still same reference 
        self <= p0.es
        self <= [p0.e0, p0.e1]
                
        self.assertRaises(pod.core.PodObjectUndefined, getattr, p0.e0, 'title')
        self.assertRaises(pod.core.PodObjectUndefined, getattr, p0.e1, 'title')
        
        self <= p0.t0
        self <= None

        self <= p1.t1
        self <= None
        
        db.commit(close = True)
         
    def nsud_undefined_no_error(self):

        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            age      = pod.typed.Int()
            is_alive = pod.typed.Object() 
            
        self <= len([peep for peep in Person]) 
        self <= 2

        p0 = [peep for peep in Person][0]
        p1 = [peep for peep in Person][1]

        self <= p1.p0
        self <= p0

        self <= p0.e0.get_full_id()
        self <= p1.e0.get_full_id()

        self <= p0.e0
        self <= p1.e0
     
        # Make sure Undefined instances are still same reference 
        self <= p0.es
        self <= [p0.e0, p0.e1]
                
                
        class Email(pod.Object):
            title = pod.typed.String()
        
        self <= p0.e0.title
        self <= 'HI'

        p0.e1.title = 'ADIOS'
        
        self <= p0.e1.title
        self <= 'ADIOS'
        
        db.commit(close = True)
    
    def nsud_check_if_exists_0(self):

        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            age      = pod.typed.Int()
            is_alive = pod.typed.Object() 
            
        self <= len([peep for peep in Person]) 
        self <= 2

        p0 = [peep for peep in Person][0]
        p1 = [peep for peep in Person][1]

        self <= p1.p0
        self <= p0

        self <= p0.e0.get_full_id()
        self <= p1.e0.get_full_id()

        self <= p0.e0
        self <= p1.e0

        @Tester.raises(pod.core.PodObjectUndefined)
        def fn():
            self <= p0.e0.title
            self <= None
        
        db.commit(close = True)
        
    def nsud_check_if_exists_1(self):

        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            age      = pod.typed.Int()
            is_alive = pod.typed.Object() 
            
        self <= len([peep for peep in Person]) 
        self <= 2

        p0 = [peep for peep in Person][0]
        p1 = [peep for peep in Person][1]

        self <= p1.p0
        self <= p0

        self <= p0.e0.get_full_id()
        self <= p1.e0.get_full_id()

        self <= p0.e0
        self <= p1.e0

        @Tester.raises(pod.core.PodObjectUndefined)
        def fn():
            self <= getattr(p0.e0, 'no_attr', None)
            self <= None
        
        db.commit(close = True)
        
    def nsud_delete_attr(self):

        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = True)
        
        class Person(pod.Object):
            age      = pod.typed.Int()
            is_alive = pod.typed.Object() 
            
        class Skater(Person):
            weight = pod.typed.Float()

        class Email(pod.Object):
            title = pod.typed.String()
                
        p0 = pod.Query(where = Person, limit = 1).get_one()
        
        self <= [p0.e0, p0.e1]
        self <= p0.es

        p1 = [peep for peep in Person][1]

        self <= p0.e0
        self <= p1.e0
                
        # set e0 to have a mutable . . . 
        p0.e0.a_list = [10, 20, 30]
                        
        for e in p0.es:
            e.delete()
        
        self <= [e for e in Email]
        self <= []
        
        self <= object.__getattribute__(p0.e0, '__class__')
        self <= pod.core.Deleted

        self <= [p0.e0, p0.e1]
        self <= p0.es
                    
        self.assertRaises(pod.core.PodObjectDeleted, getattr, p0.e0, 'title')
        self.assertRaises(pod.core.PodObjectDeleted, getattr, p0.e1, 'title')
        
        @Tester.raises(pod.core.PodObjectDeleted)
        def fn():
            p0.e0.name = 'Fred'
                                        
        p1 = [peep for peep in Person][1]

        self <= p0.e0
        self <= p1.e0
        
        db.commit(close = True)

    def nsud_deleted_attr_access(self):

        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            age      = pod.typed.Int()
            is_alive = pod.typed.Object() 
            
        class Skater(Person):
            weight = pod.typed.Float()

        class Email(pod.Object):
            title = pod.typed.String()
        
        p0 = pod.Query(where = Person, limit = 1).get_one()

        self <= object.__getattribute__(p0.e0, '__class__')
        self <= Email
        
        @Tester.raises(pod.core.PodObjectDeleted)
        def fn():   
            p0.e0.title                                 # This function call a typed attribute, so throws error in load_attr_from_db

        @Tester.raises(pod.core.PodObjectDeleted)
        def fn():   
            getattr(p0.e1, 'some_random_attr', None)    # This function calls a dynamic attribute, so throws error in inst_check_if_exists
                            
       
        self <= object.__getattribute__(p0.e0, '__class__')
        self <= Email
        self <= object.__getattribute__(p0.e1, '__class__')
        self <= Email
        
        self <= p0.es
        self <= [None, None]
                   
        @Tester.raises(pod.core.PodObjectDeleted)
        def fn():
            p0.e0.title

        @Tester.raises(pod.core.PodObjectDeleted)
        def fn():
            p0.e0['title']

        @Tester.raises(pod.core.PodObjectDeleted)
        def fn():
            p0.e0.full_load()
                                               
        db.commit(close = True)

""" RAW COLLECTIONS """    
class Collections(Tester): 
           
    def test_collections(self):
        # list
        self.col_list_section_0()
        self.clear_mod_imports()
        self.col_list_section_1()
        self.clear_mod_imports()
        self.col_list_section_2()
        self.clear_mod_imports()
        self.col_list_section_3()
        self.clear_mod_imports()
        
        # dict
        self.col_dict_section()
        self.clear_mod_imports()
        
        # set
        self.col_set_section()
        self.clear_mod_imports()
        self.col_all_section()
        
    def col_list_section_0(self):
        import pod
        import pod.list
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
                
        self <= [i for i in pod.list.List]
        self <= []
        
        a_list = pod.list.List()        
        self.assertRaises(IndexError, a_list.__getitem__, 0)
        
        class Person(pod.Object):
            pass
        
        peep = Person()
        
        b_list = pod.list.List([1, 2, 'k', {'peep': peep}, peep])
        
        self <= [i for i in b_list]
        self <= [1, 2, 'k', {'peep': peep}, peep]
        
        c_list = pod.list.List([0, 1, 2, 3, 4, 5, 6])
        c_list.append(7)
        c_list.extend([8, 9])
        
        self <= [i for i in c_list]
        self <= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        self <= (2 in c_list)
        self <= True
        
        self <= ('2' in c_list)
        self <= False

        c_list.remove(4)
        self <= [i for i in c_list]
        self <= [0, 1, 2, 3, 5, 6, 7, 8, 9]

        c_list.insert(4, 4)
        self <= [i for i in c_list]
        self <= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        
        c_list.reverse()
        self <= [i for i in c_list]
        self <= [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

        c_list.reverse()
        self <= [i for i in c_list]
        self <= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        
        self <= c_list[4]
        self <= 4

        self <= c_list[-2]
        self <= 8

        self <= c_list[3:]
        self <= [3, 4, 5, 6, 7, 8, 9]

        self <= c_list[3::2]
        self <= [3, 5, 7, 9]
        
        self <= c_list[3:8:3]
        self <= [3, 6]
        
        self <= c_list[-3:-8:-1]
        self <= [7, 6, 5, 4, 3]
        
        self <= c_list[-3:-8:-2]
        self <= [7, 5, 3]
        
        self <= len(c_list)
        self <= 10

        c_list.clear()
        self <= [i for i in c_list]
        self <= []
        
        db.commit(close = True)
        
    def col_list_section_1(self):
        import pod
        import pod.list
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        a_list = pod.list.List(name = 'A_LIST')        
        self.assertRaises(IndexError, a_list.__getitem__, 0)
        
        class Person(pod.Object):
            pass
        
        peep = Person()
        
        a_list.append(peep)
        a_list.append(peep)
        
        self <= a_list.copy()
        self <= [peep, peep]
        
        a_list.remove(peep)       
        self <= a_list.copy()
        self <= [peep]

        self <= a_list.pop()
        self <= peep

        self <= a_list.copy()
        self <= []
        
        a_list.append('you_know_it')
        a_list.append({10: 20})
        a_list.append(10)

        db.commit(close = True)
    
    def col_list_section_2(self):
        
        import pod
        import pod.list
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        e_list = pod.list.List()    
        
        self <= len(e_list)
        self <= 0
        
        e_list.append(10)
        e_list.append(20)
        e_list.append(e_list)

        self <= e_list[5:4]
        self <= []
        
        @Tester.raises(pod.list.PodListError)
        def fn():
            e_list[[10]]
            
        e_list[0] = 20
        e_list[1] = 10
        
        self <= [e for e in e_list]
        self <= [20, 10, e_list]
        
        some_list = [1, 2, 3]
        some_list[0:2] = [30,40]
        
        self <= some_list
        self <= [30, 40, 3]
        
        e_list[0:2] = [30, 40]
        
        self <= [e for e in e_list]
        self <= [30, 40, e_list]

        del e_list[0]
        del e_list[0:2]

        self <= [e for e in e_list]
        self <= []
            
        self <= e
        self <= e_list
            
        e_list.append(10)
        e_list[0] = 'hi'
        
        self <= e_list.copy()
        self <= ['hi']
        
        @Tester.raises(pod.list.PodListError)
        def fn():
            e.remove('10')
        
        e.remove('hi')
        
        self <= e.copy()
        self <= []

        e.append(10)
        e.append(20)
        e.append(10)
        e.append(e_list)
        
        self <= e.count(10)
        self <= 2

        self <= e.count(20)
        self <= 1

        self <= e.count(e_list)        
        self <= 1
        
        self <= e.count(30)
        self <= 0
        
        e.remove(e_list)
        e.sort()
        
        self <= e.copy()
        self <= [10, 10, 20]
        
        self <= e.index(10)
        self <= 0
        
        self <= e.index(20)
        self <= 2
        
        
        @Tester.raises(pod.list.PodListError)
        def fn():
            e.index(50)
        
        # NOW DELETE
        e_list.delete()
        
        @Tester.raises(pod.core.PodObjectDeleted)
        def fn():
            for e in e_list.__iter__():
                print e

        db.commit(close = True)

    def col_list_section_3(self):
        import pod
        import pod.list

        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        a_list = (pod.list.List.where.name == 'A_LIST').get_one()     
        
        self <= a_list.copy()
        self <= ['you_know_it', {10: 20}, 10]

        db.commit(close = True)

    def col_dict_section(self):
        import pod
        import pod.dict
        db = pod.Db(file = self.filename, remove = False)
        
        a_dict = pod.dict.Dict(dict = {10: 20}, name = 'A_DICT')
        b_dict = pod.dict.Dict(dict = {20: 20}, name = 'B_DICT')
        
        self.assertRaises(KeyError, a_dict.__getitem__, 'no_key')
        
        a_dict['some_key'] = [10, 20, 30]

        self <= a_dict.copy()
        self <= {10: 20, 'some_key': [10, 20, 30]}
        
        a_dict[10] = '10'
        
        self <= a_dict.keys()
        self <= [10, 'some_key']
        
        a_dict.update({'10': 10})

        self <= a_dict.items()
        self <= [(10, '10'), ('some_key', [10, 20, 30]), ('10', 10)]

        self <= a_dict.values()
        self <= ['10', [10, 20, 30], 10]
        
        a_dict.clear()
        self <= a_dict.copy()
        self <= {}
        
        a_dict['foo'] = 'bar'
        self <= a_dict.copy()
        self <= {'foo': 'bar'}

        self <= len(a_dict)
        self <= 1
        
        self <= ('foo' in a_dict)
        self <= True
        
        self <= ('you' in a_dict)
        self <= False
        
        a_dict['me'] = a_dict
        a_dict['b']  = 3
        
        self <= set([key for key in a_dict])
        self <= set(['me', 'foo', 'b'])
        
        self <= set([key for key in a_dict.iterkeys()])
        self <= set(['me', 'foo', 'b'])
        
        self <= a_dict.get('foo', 10)
        self <= 'bar'
        
        self <= a_dict.get('you')
        self <= None

        self <= a_dict.get('moo', a_dict)
        self <= a_dict
        
        del a_dict['me']
        
        self <= set([key for key in a_dict.itervalues()])
        self <= set([3, 'bar'])
        
        a_dict.setdefault('foo', 20)

        self <= set([key for key in a_dict.itervalues()])
        self <= set([3, 'bar'])
                
        a_dict.setdefault('moo', 30)
        
        self <= set([key for key in a_dict.itervalues()])
        self <= set([3, 'bar', 30])
        
        del a_dict['moo']
        del a_dict['b']
        
        @Tester.raises(KeyError)
        def fn():
            del a_dict['asdf']
        @Tester.raises(pod.dict.PodDictError)
        def fn():
            a_dict.fromkeys()
        @Tester.raises(pod.dict.PodDictError)
        def fn():
            a_dict.has_key()
        @Tester.raises(pod.dict.PodDictError)
        def fn():
            a_dict.pop('key')
        @Tester.raises(pod.dict.PodDictError)
        def fn():
            a_dict.popitem()

        db.commit(close = True)

    def col_set_section(self):
        import pod
        import pod.set
        db = pod.Db(file = self.filename, remove = False)
        
        a_set = pod.set.Set(set = [1, 2, 3], name = 'A_SET')
        b_set = pod.set.Set(set = [123, 342, 234])

        self  <=  a_set.copy()
        self  <=  set([1, 2, 3])

        self  <=  [i for i in a_set]
        self  <=  [1, 2, 3]
        
        a_set.add(2)
        
        self  <=  [i for i in a_set]
        self  <=  [1, 2, 3]

        a_set.remove(2)
        
        self  <=  [i for i in a_set]
        self  <=  [1, 3]
                
        class Person(pod.Object):
            pass
        
        peep_0 = Person()
        peep_1 = Person()
        
        a_set.add(peep_0)
        a_set.add(peep_1)
        a_set.add(peep_0)
        a_set.add(peep_1)
        a_set.add(peep_0)
        a_set.add(peep_1)
        
        self  <=  [i for i in a_set]
        self  <=  [1, 3, peep_0, peep_1]
        
        a_set.remove(peep_0)

        self  <=  [i for i in a_set]
        self  <=  [1, 3, peep_1]

        a_set.add(peep_0)

        self  <=  [i for i in a_set]
        self  <=  [1, 3, peep_1, peep_0]

        self  <=  a_set | (10, peep_0)
        self  <=  set([peep_0, 1, 10, 3, peep_1])

        self  <=  a_set & (3, 10, peep_1)
        self  <=  set([3, peep_1])
        
        self.assertRaises(KeyError, a_set.remove, 20)
        a_set.discard(20)
        a_set.discard(peep_1)

        self  <=  [i for i in a_set]
        self  <=  [1, 3, peep_0]
        
        a_set.remove(peep_0)

        self  <=  [i for i in a_set]
        self  <=  [1, 3]
        
        self  <=  a_set.copy()
        self  <=  set([1, 3])
        
        
        c_set = pod.set.Set()

        self <= len(c_set)
        self <= 0

        c_set.add(0)
        c_set.add(1)
        c_set.add(1)
        c_set.add(c_set)
        c_set.add(self)

        d_set = pod.set.Set()

        self <= len(d_set)
        self <= 0

        d_set.add(0)
        d_set.add(1)
        d_set.add(1)
        d_set.add(c_set)
        d_set.add(self)

        # now play with c_set again
        
        self <= (self in c_set)
        self <= True
        
        self <= ('k' in c_set)
        self <= False

        c_set.remove(self)
        c_set.remove(c_set)
                
        self <= (self in c_set)
        self <= False
        
        self <= (c_set in c_set)
        self <= False
        
        self <= set([c_set.pop(), c_set.pop()])
        self <= set([0, 1])

        self <= c_set.copy()
        self <= set([])
        
        
        @Tester.raises(KeyError)
        def fn():
            c_set.pop()

        c_set.add(10)
        c_set.add(10)
        c_set.add(self)
        c_set.add(c_set)

        c_set.clear()
        
        self <= c_set.copy()
        self <= set()
        
        e_set = pod.set.Set([1, 2, 3])
        x_set = pod.set.Set([1, 2, 3, 4, 5])

        self <= (e_set <= x_set)
        self <= True

        self <= (e_set <= x_set.copy())
        self <= True

        self <= (e_set < x_set)
        self <= True

        self <= (e_set < x_set.copy())
        self <= True

        self <= (e_set < e_set)
        self <= False

        self <= (e_set < e_set.copy())
        self <= False
        
        """ > """
        self <= (e_set > x_set)
        self <= False
        
        self <= (e_set > x_set.copy())
        self <= False

        self <= (e_set >= x_set)
        self <= False
        
        self <= (e_set >= x_set.copy())
        self <= False
        
        self <= (x_set > e_set)
        self <= True
        
        self <= (x_set > e_set.copy())
        self <= True

        self <= (x_set >= e_set)
        self <= True
        
        self <= (x_set >= e_set.copy())
        self <= True

        self <= (x_set >= x_set)
        self <= True

        self <= (x_set >= x_set.copy())
        self <= True

        self <= (x_set > x_set)
        self <= False

        self <= (x_set > x_set.copy())
        self <= False 
        
        """ MINUS """        
        self <= (x_set - e_set)
        self <= set([4, 5])

        self <= (e_set - x_set)
        self <= set([])
        
        """ XOR """
        self <= (e_set ^ x_set)
        self <= set([4, 5])

        self <= (x_set ^ e_set)
        self <= set([4, 5])

        """ AND OR """
        self <= (e_set & x_set)
        self <= set([1, 2, 3])
        
        self <= (e_set & x_set.copy())
        self <= set([1, 2, 3])
        
        self <= (e_set | x_set)
        self <= set([1, 2, 3, 4, 5])
        
        self <= (e_set | x_set.copy())
        self <= set([1, 2, 3, 4, 5])

        @Tester.raises(pod.set.PodSetError)
        def fn():
            c_set.isdisjoint(set())

        @Tester.raises(pod.set.PodSetError)
        def fn():
            c_set.intersection_update(set())

        @Tester.raises(pod.set.PodSetError)
        def fn():
            c_set.difference_update(set())
        
        @Tester.raises(pod.set.PodSetError)
        def fn():
            c_set.symmetric_difference_update(set())
        
        db.commit(close = True)

        

    def col_all_section(self):
        import pod
        import pod.dict
        db = pod.Db(file = self.filename, remove = False)

        a_dict = (pod.dict.Dict.where.name == 'A_DICT').get_one()     
        
        self <= a_dict.values()
        self <= ['bar']
        
        a_dict['baz'] = [10, 20, {'10': '20'}]

        self <= a_dict.copy()
        self <= {'foo': 'bar', 'baz': [10, 20, {'10': '20'}]}
        
        db.commit(clear_cache = True)

        a_dict = (pod.dict.Dict.where.name == 'A_DICT').get_one()     

        self <= a_dict.copy()
        self <= {'foo': 'bar', 'baz': [10, 20, {'10': '20'}]}
        
        temp = a_dict['baz'][2]
        self <= temp
        self <= {'10': '20'}
        
        temp['10'] = '10'
        self <= temp
        self <= {'10': '10'}

        self <= a_dict.copy()
        self <= {'foo': 'bar', 'baz': [10, 20, {'10': '10'}]}
        
        temp['10'] = 'FOOBARBAZ'

        self <= a_dict.copy()
        self <= {'foo': 'bar', 'baz': [10, 20, {'10': 'FOOBARBAZ'}]}

        self <= a_dict['baz'][2]
        self <= {'10': 'FOOBARBAZ'}

        db.commit(clear_cache = True)

        a_dict = (pod.dict.Dict.where.name == 'A_DICT').get_one()  
        
        self <= a_dict.copy()
        self <= {'foo': 'bar', 'baz': [10, 20, {'10': 'FOOBARBAZ'}]}

        db.commit(close = True)

""" TIME MODULES """
class Timers(Tester):

    def test_time_types(self):
        Timer = self.time_create_section(remove = True)
        self.time_section_0(Timer)
        self.clear_mod_imports()
        Timer = self.time_create_section(remove = False)
        self.time_section_1(Timer)

    def time_create_section(self, remove):
 
        import pod
        import time    # You don't need to import time -- we're just going to use it below for our examples . . . 
        
        db = pod.Db(file = self.filename, remove = remove)
        
        class Timer(pod.Object):
            
            sometime  = pod.typed.Time(date_string = '%Y-%m-%d')
            uponatime = pod.typed.Time()
            utctime   = pod.typed.Time(local_time = False)
            ctime     = pod.typed.TimeCreate()
            mtime     = pod.typed.TimeLoad(date_string = '%Y-%m-%d %H:%M:%S')
            
        t0 = Timer(sometime = '2007-1-15')
        t1 = Timer(sometime = '2003-2-25')
        t2 = Timer(sometime = '2009-4-27')
        t3 = Timer(sometime = '2009-1-11')
        
        # For the uponatime field, you have to provide a valid UTC time stamp since you didn't set a date_string in the constructor
        t0.uponatime = Timer.uponatime.convert_time_to_utc_stamp(value = 2348923, local_time = True)
        t1.uponatime = Timer.uponatime.convert_time_to_utc_stamp(value = 324234, local_time = False)
        t2.uponatime = Timer.uponatime.convert_time_to_utc_stamp(value = '01/05/2006', date_string = '%d/%m/%Y', local_time = True)
        t3.uponatime = Timer.uponatime.convert_time_to_utc_stamp(value = '02/04/2006 12:00:00', date_string = '%d/%m/%Y %H:%M:%S', local_time = True)
        
        db.commit(clear_cache = True)
        
        return Timer
    
    def time_section_0(self, Timer):
        
        self.assertEqual(Timer.get_count(), 4)
        
        self.assertEqual(set([t.sometime for t in Timer.sometime.is_date(year = 2009)]), set(['2009-04-27','2009-01-11']))
            
        self.assertEqual(Timer.sometime.is_date(year = 2009, month = 4).get_one().sometime, '2009-04-27')
                
        set_one = set([t.sometime for t in Timer.sometime.post_dates(year = 2006) & Timer.sometime.pre_dates(year = 2008)])
        set_two = set(['2007-01-15'])

        self.assertEquals(set_one, set_two)
    
        Timer.get_db().commit(close = True)

    def time_section_1(self, Timer):
        self.assertEqual(Timer.get_count(), 8)
        
        self.assertEqual(set([t.sometime for t in Timer.sometime.is_date(year = 2009)]), set(['2009-04-27','2009-01-11']))
            
        self.assertEqual([t.sometime for t in Timer.sometime.is_date(year = 2009, month = 4)], ['2009-04-27', '2009-04-27'])
                
        list_one = [t.sometime for t in Timer.sometime.post_dates(year = 2006) & Timer.sometime.pre_dates(year = 2008)]
        list_two = ['2007-01-15','2007-01-15']

        self.assertEquals(list_one, list_two)
    
        Timer.get_db().commit(close = True)

""" RAW QUERIES """
class Raw(Tester):         
    
    def test_raw_query(self):
        
        import pod

        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
           name = pod.typed.String()
           age  = pod.typed.Int()
                
        p0 = Person(name = 'Ali', age = 20)
        p1 = Person(name = 'Jin', age = 25)
        p2 = Person(name = 'Eli', age = 50)
        
        db.commit()
        
        self.assertEquals((2*Person.age+1 > 100).get_one().name, 'Eli')
        
        if pod.db.PY_VERSION_IS_25 is False:
            self.assertEquals((pod.fn.replace(Person.name, 'in', 'oe') == 'Joe').get_one().name, 'Jin')
            self.assertEquals(set([p.name for p in pod.fn.replace(Person.name, 'li', 'mi').endswith('i')]), set(['Ali', 'Eli']))
        
        class Customer(pod.Object):
            name     =   pod.typed.String()
        
        class Order(pod.Object):
            cust_id  =   pod.typed.Int()    
            amount   =   pod.typed.Float()
            month    =   pod.typed.String()
            
        cust_0 = Customer(name = 'Abi')
        cust_1 = Customer(name = 'Jin')
        cust_2 = Customer(name = 'Ira')
            
        order_0 = Order(cust_id = cust_0.id,  amount = 10.99,  month = 'Jan')
        order_1 = Order(cust_id = cust_1.id,  amount =  3.99,  month = 'Jan')
        order_2 = Order(cust_id = cust_0.id,  amount =  1.99,  month = 'Jan')
        order_3 = Order(cust_id = cust_0.id,  amount =  1.99,  month = 'Feb')
        order_4 = Order(cust_id = cust_2.id,  amount = 23.99,  month = 'Feb')
        order_5 = Order(cust_id = cust_0.id,  amount =  4.99,  month = 'Feb')
        order_6 = Order(cust_id = cust_2.id,  amount =  4.29,  month = 'Feb')
        order_7 = Order(cust_id = cust_0.id,  amount = 10.00,  month = 'Mar')
        order_8 = Order(cust_id = cust_1.id,  amount = 20.99,  month = 'Mar')
        order_9 = Order(cust_id = cust_1.id,  amount = 20.99,  month = 'Mar')
        
        db.commit()
        
        query = pod.RawQuery(select   = pod.fn.avg(Order.amount) >> 'avg' | Order.month, 
                             group_by = Order.month, 
                             )

        self.assertEqual(set([(row.avg,row.month) for row in query]), set([(5.6566666666666663, 'Jan'), (17.326666666666664, 'Mar'), (8.8149999999999995, 'Feb')]))
        
        for row in query:
            self.assertEqual(row.avg, row[0])
            self.assertEqual(row.avg, row['avg'])
            self.assertEqual(row.month, row[1])
            self.assertEqual(row.month, row['month'])
            
        query = pod.RawQuery(select   = Customer.name | Order.amount | Order.month,
                             where    = (Order.amount > 10) & (Customer.id == Order.cust_id), 
                             )

        self <= [(row.name,row.amount,row.month) for row in query]
        self <= [('Abi', 10.99, 'Jan'), ('Ira', 23.989999999999998, 'Feb'), ('Jin', 20.989999999999998, 'Mar'), ('Jin', 20.989999999999998, 'Mar')]
                       
        class MonthlyAvg(pod.Object):
            month = pod.typed.String()
            avg   = pod.typed.Float()
            
        query = pod.RawQuery(select   = pod.fn.avg(Order.amount) >> 'avg' | Order.month, 
                             group_by = Order.month, 
                             cls      = MonthlyAvg,
                             )
        
        self.assertEqual(set([(row.avg,row.month) for row in query]), set([(5.6566666666666663, 'Jan'), (17.326666666666664, 'Mar'), (8.8149999999999995, 'Feb')]))

            
        query = pod.RawQuery(select   = pod.fn.avg(Order.amount) >> 'avg' | Order.month, 
                             group_by = Order.month, 
                             cls      = MonthlyAvg,
                             )
        
        for monthly_avg in query:
            monthly_avg
            monthly_avg.avg,  monthly_avg.month
            
        db.commit(close = True)   # You need to commit these new 'MonthlyAvg' objects to the database. 

""" RAW COLLECTIONS """    
class AltSchema(Tester): 
           
    def test_schema(self):
        # list
        self.alt_schema_0()
        self.clear_mod_imports()
        self.alt_schema_1()
        self.clear_mod_imports()
        self.alt_schema_2()
        self.clear_mod_imports()
        self.alt_schema_3()
        self.clear_mod_imports()
        
    def alt_schema_0(self):
        import pod
        db = pod.Db(file = self.filename, dynamic_index = True, remove = True, chatty = True)
        
        class Person(pod.Object):
            name   = pod.typed.String(index = False)
            age    = pod.typed.Int(index = True)
            emails = pod.typed.Object(index = True)
            skater = pod.typed.PodObject(index = True)

        class Skater(Person):
            peep   = pod.typed.PodObject(index = False)

        skater = None

        for i in range(12):
            peep   = Person(name = 'Fred' + str(i), age = i, skater = skater, emails = [1, 2, 3])
            skater = Skater(name = 'Maos' + str(i), age = i, skater = skater, emails = [peep, {10: 20}, (1,2,3)], peep = peep)
            
        self <= [peep.name for peep in (Person.name[4:6] == '10') & (Person.age < 102)]
        self <= ['Fred10', 'Maos10']
        
        self <= len([skater.peep for skater in Skater if skater.peep.name[4:6] == '10'])
        self <= 1
        
        self <= set(Person.pod.type_get_current_table_indexes())
        self <= set(['emails', 'skater', 'age'])

        self <= set(Skater.pod.type_get_current_table_indexes())
        self <= set(['skater', 'age', 'emails'])

        self <= set(Person.pod.type_get_current_table_dict_indexes())
        self <= set(['__main___Person_kvdict_pin_value', 'sqlite_autoindex___main___Person_kvdict_1'])
        
        self <= set(Skater.pod.type_get_current_table_dict_indexes())
        self <= set(['__main___Skater_kvdict_pin_value', 'sqlite_autoindex___main___Skater_kvdict_1'])
        
        # Fake out mtime parameters
        db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Person.pod.id,)) 
        db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Skater.pod.id,)) 
        
        db.commit(close = True)
        
    def alt_schema_1(self):
        
        import pod
        db = pod.Db(file = self.filename, dynamic_index = False, remove = False, chatty = True, very_chatty = False)
        
        class Person(pod.Object):
            pass
        
        class Skater(Person):
            pass
        
        self <= [peep.name for peep in (Person.where.name[4:6] == '10') & (Person.where.age < 102)]
        self <= ['Fred10', 'Maos10']
        
        self <= len([skater.peep for skater in Skater if skater.peep.name[4:6] == '10'])
        self <= 1
        
        self <= set(Person.pod.type_get_current_table_indexes())
        self <= set([])

        self <= set(Skater.pod.type_get_current_table_indexes())
        self <= set([])

        self <= set(Person.pod.type_get_current_table_dict_indexes())
        self <= set(['sqlite_autoindex___main___Person_kvdict_1'])
        
        self <= set(Skater.pod.type_get_current_table_dict_indexes())
        self <= set(['sqlite_autoindex___main___Skater_kvdict_1'])
        
        #Fake out mtime parameters
        db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Person.pod.id,)) 
        db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Skater.pod.id,)) 
    
        db.commit(close = True)

    def alt_schema_2(self):
        import pod
        db = pod.Db(file = self.filename, remove = False, chatty = True)
        
        class Person(pod.Object):
            POD_DYNAMIC_INDEX = True
            name = pod.typed.String(index = False)
            age  = pod.typed.Int(index = True)

        class Skater(Person):
            peep = pod.typed.PodObject(index = False)
            
        self <= [peep.name for peep in (Person.name[4:6] == '10') & (Person.age < 102)]
        self <= ['Fred10', 'Maos10']
        
        self <= len([skater.peep for skater in Skater if skater.peep.name[4:6] == '10'])
        self <= 1
        
        self <= set(Person.pod.type_get_current_table_indexes())
        self <= set(['age'])

        self <= set(Skater.pod.type_get_current_table_indexes())
        self <= set(['age'])

        self <= set(Person.pod.type_get_current_table_dict_indexes())
        self <= set(['__main___Person_kvdict_pin_value', 'sqlite_autoindex___main___Person_kvdict_1'])
        
        self <= set(Skater.pod.type_get_current_table_dict_indexes())
        self <= set(['__main___Skater_kvdict_pin_value', 'sqlite_autoindex___main___Skater_kvdict_1'])
        
        # Fake out mtime parameters
        db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Person.pod.id,)) 
        db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Skater.pod.id,)) 
        
        db.commit(close = True)

    def alt_schema_3(self):
        import pod
        db = pod.Db(file = self.filename, remove = False, chatty = True)

        class Person(pod.Object):
            POD_DYNAMIC_INDEX = False
            name   = pod.typed.String(index = True)
            age    = pod.typed.Int(index = False)
            emails = pod.typed.Object(index = True)
            skater = pod.typed.PodObject(index = True)
        
        class Skater(Person):
            pass
            
        self <= [peep.name for peep in (Person.name[4:6] == '10') & (Person.age < 102)]
        self <= ['Fred10', 'Maos10']
        
        self <= len([skater.peep for skater in Skater if skater.peep.name[4:6] == '10'])
        self <= 1
        
        # Fake out mtime parameters
        #db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Person.pod.id,)) 
        #db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Skater.pod.id,)) 
        
        db.commit(close = True)

    def test_dynamic_index(self):
        self.dynamic_index_0()
        self.clear_mod_imports()
        self.dynamic_index_1()
        self.clear_mod_imports()
        self.dynamic_index_2()
        self.clear_mod_imports()

    def dynamic_index_0(self):
        
        import pod
        db = pod.Db(file = self.filename, dynamic_index = True, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            age = pod.typed.Int(index = True)
            
        class Punk(Person):
            weight = pod.typed.Float(index = True)
                 
        peep = Person(name = 'Fred', age = 20, some_attr = 'Name')
        punk = Punk(name = 'Tony', age = 19, some_other_attr = 'Donny')
        punk = Punk(name = 'Jony', age = 13, some_other_attr = 'Bonny')
        punk = Punk(name = 'Mony', age = 13, some_other_attr = 'Ronny')
                
        self <= Person.pod.type_get_current_table_indexes()
        self <= ['age']

        self <= Person.pod.type_get_current_table_dict_indexes()
        self <= ['__main___Person_kvdict_pin_value', 'sqlite_autoindex___main___Person_kvdict_1']
            
        db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Person.pod.id,)) 
        db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Punk.pod.id,)) 
                
        db.commit(close = True)

    def dynamic_index_1(self):
        
        import pod
        db = pod.Db(file = self.filename, dynamic_index = False, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            age = pod.typed.Int(index = False)
            
        class Punk(Person):
            POD_DYNAMIC_INDEX = True
            weight = pod.typed.Float(index = False)
                 
        peep = Person(name = 'Fred', age = 20, some_attr = 'Name')
        punk = Punk(name = 'Tony', age = 19, some_other_attr = 'Donny')
        punk = Punk(name = 'Jony', age = 13, some_other_attr = 'Bonny')
        punk = Punk(name = 'Mony', age = 13, some_other_attr = 'Ronny')
                
        self <= Person.pod.type_get_current_table_indexes()
        self <= []

        self <= Punk.pod.type_get_current_table_indexes()
        self <= []

        self <= Person.pod.type_get_current_table_dict_indexes()
        self <= ['sqlite_autoindex___main___Person_kvdict_1']
        
        self <= Punk.pod.type_get_current_table_dict_indexes()
        self <= ['__main___Punk_kvdict_pin_value', 'sqlite_autoindex___main___Punk_kvdict_1']
                
        db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Person.pod.id,)) 
        db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Punk.pod.id,)) 

        db.commit(close = True)

    def dynamic_index_2(self):

        import pod
        db = pod.Db(file = self.filename, dynamic_index = True, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            age = pod.typed.Int(index = False)
            
        class Punk(Person):
            POD_DYNAMIC_INDEX = False
            weight = pod.typed.Float(index = False)
                 
        peep = Person(name = 'Fred', age = 20, some_attr = 'Name')
        punk = Punk(name = 'Tony', age = 19, some_other_attr = 'Donny')
        punk = Punk(name = 'Jony', age = 13, some_other_attr = 'Bonny')
        punk = Punk(name = 'Mony', age = 13, some_other_attr = 'Ronny')
                
        self <= Person.pod.type_get_current_table_indexes()
        self <= []

        self <= Punk.pod.type_get_current_table_indexes()
        self <= []

        self <= Person.pod.type_get_current_table_dict_indexes()
        self <= ['__main___Person_kvdict_pin_value', 'sqlite_autoindex___main___Person_kvdict_1']
        
        self <= Punk.pod.type_get_current_table_dict_indexes()
        self <= ['sqlite_autoindex___main___Punk_kvdict_1']
                
        db.commit(close = True)

    def test_dynamic_index_no_file_change(self):
        self.dynamic_index_no_file_change_0()
        self.clear_mod_imports()
        self.dynamic_index_no_file_change_1()
        self.clear_mod_imports()
        self.dynamic_index_no_file_change_2()
        self.clear_mod_imports()

    def dynamic_index_no_file_change_0(self):
        import pod
        db = pod.Db(file = self.filename, dynamic_index = True, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            pass
            
        class Punk(Person):
            weight = pod.typed.Float(index = True)
                 
        peep = Person(name = 'Fred', age = 20, some_attr = 'Name')
        punk = Punk(name = 'Tony', age = 19, some_other_attr = 'Donny')
        punk = Punk(name = 'Jony', age = 13, some_other_attr = 'Bonny')
        punk = Punk(name = 'Mony', age = 13, some_other_attr = 'Ronny')
                
        self <= Person.pod.type_get_current_table_indexes()
        self <= []

        self <= Person.pod.type_get_current_table_dict_indexes()
        self <= ['__main___Person_kvdict_pin_value', 'sqlite_autoindex___main___Person_kvdict_1']

        self <= Punk.pod.type_get_current_table_indexes()
        self <= ['weight']

        self <= Punk.pod.type_get_current_table_dict_indexes()
        self <= ['__main___Punk_kvdict_pin_value', 'sqlite_autoindex___main___Punk_kvdict_1']
                            
        db.commit(close = True)

    def dynamic_index_no_file_change_1(self):
        
        import pod
        db = pod.Db(file = self.filename, dynamic_index = False, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            pass
            
        class Punk(Person):
            weight = pod.typed.Float(index = True)
                 
        peep = Person(name = 'Fred', age = 20, some_attr = 'Name')
        punk = Punk(name = 'Tony', age = 19, some_other_attr = 'Donny')
        punk = Punk(name = 'Jony', age = 13, some_other_attr = 'Bonny')
        punk = Punk(name = 'Mony', age = 13, some_other_attr = 'Ronny')
                
        self <= Person.pod.type_get_current_table_indexes()
        self <= []

        self <= Person.pod.type_get_current_table_dict_indexes()
        self <= ['sqlite_autoindex___main___Person_kvdict_1']

        self <= Punk.pod.type_get_current_table_indexes()
        self <= ['weight']

        self <= Punk.pod.type_get_current_table_dict_indexes()
        self <= ['sqlite_autoindex___main___Punk_kvdict_1']

        db.commit(close = True)

    def dynamic_index_no_file_change_2(self):
        
        import pod
        db = pod.Db(file = self.filename, dynamic_index = True, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            pass
            
        class Punk(Person):
            weight = pod.typed.Float(index = True)
                 
        peep = Person(name = 'Fred', age = 20, some_attr = 'Name')
        punk = Punk(name = 'Tony', age = 19, some_other_attr = 'Donny')
        punk = Punk(name = 'Jony', age = 13, some_other_attr = 'Bonny')
        punk = Punk(name = 'Mony', age = 13, some_other_attr = 'Ronny')
                
        self <= Person.pod.type_get_current_table_indexes()
        self <= []

        self <= Person.pod.type_get_current_table_dict_indexes()
        self <= ['__main___Person_kvdict_pin_value', 'sqlite_autoindex___main___Person_kvdict_1']

        self <= Punk.pod.type_get_current_table_indexes()
        self <= ['weight']

        self <= Punk.pod.type_get_current_table_dict_indexes()
        self <= ['__main___Punk_kvdict_pin_value', 'sqlite_autoindex___main___Punk_kvdict_1']
                            
        db.commit(close = True)

""" THE BASICS """            
class PodAsDict(Tester):
  
    def test_pod_full_load(self):
        self.full_load_0()
        self.clear_mod_imports()
        self.full_load_1()
        self.clear_mod_imports()
        
    def full_load_0(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            name = pod.typed.String()
        
        class Punk(Person):    
            age = pod.typed.Int()
            
        peep = self.full_load_create(cls = Person)
        punk = self.full_load_create(cls = Punk)
    
        for inst in [peep, punk]:
            self <= set(inst.__dict__.keys())
            self <= set(['id', 'some_set', 'age', 'some_list', 'name'])

            self <= set([str(value) for value in inst.__dict__.values()])
            self <= set(['1', 'Mandrew', '[1, 2, 3]', "set(['a', 'c', 'b'])", '34'])
            
        db.commit(close = True)
        
    def full_load_1(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            name = pod.typed.String()
        
        class Punk(Person):
            age = pod.typed.Int()
        
        peep = [peep for peep in Person][0]
        punk = [punk for punk in Person][0]
            
        for inst in [peep, punk]:
            self <= set(inst.__dict__.keys())
            self <= set(['id'])

            self <= set(inst.__dict__.values())
            self <= set([1])

        for inst in [peep, punk]:
            inst.full_load()
            
            self <= set(inst.__dict__.keys())
            self <= set(['some_set', 'age', 'some_list', 'name', 'id'])

            self <= set([str(value) for value in inst.__dict__.values()])
            self <= set(['1', 'Mandrew', '[1, 2, 3]', "set(['a', 'c', 'b'])", '34'])
                        
        db.commit(close = True)
        
    def full_load_create(self, cls):
        inst = cls(name = 'Mandrew', age = 34)
        inst['some_list'] = [1, 2, 3]
        inst.some_set = set(['a', 'b', 'c'])
        return inst
    
    def test_pod_as_dict(self):
        self.pod_as_dict_0()
        self.clear_mod_imports()
        self.pod_as_dict_1()
        self.clear_mod_imports()
        self.pod_as_dict_2()
        self.clear_mod_imports()
        self.pod_as_dict_3()
        self.clear_mod_imports()
        self.pod_as_dict_4()
        self.clear_mod_imports()
        self.pod_as_dict_5()
        self.clear_mod_imports()
    
    def pod_as_dict_0(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            name = pod.typed.String()
            
        class Punk(Person):
            age = pod.typed.Int()

        peep = self.pod_as_dict_create(cls = Person)
        punk = self.pod_as_dict_create(cls = Punk)

        self.pod_as_dict_exercise(inst = peep)
        self.pod_as_dict_exercise(inst = punk)

        db.commit(clear_cache = True)
        
        peep = [peep for peep in Person][0]
        punk = [punk for punk in Punk][0]

        self.pod_as_dict_exercise(inst = peep)
        self.pod_as_dict_exercise(inst = punk)
        
        db.commit(close = True)
        
    def pod_as_dict_1(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            name = pod.typed.String()
        
        class Punk(Person):
            age = pod.typed.Int()
        
        peep = [peep for peep in Person][0]
        punk = [punk for punk in Punk][0]

        self.pod_as_dict_exercise(inst = peep)
        self.pod_as_dict_exercise(inst = punk)
        
        db.commit(close = True)

    def pod_as_dict_2(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            name = pod.typed.String()
        
        class Punk(Person):
            age = pod.typed.Int()
        
        peep = [peep for peep in Person][0]
        punk = [punk for punk in Punk][0]

        peep.full_load()
        punk.full_load()

        self.pod_as_dict_exercise(inst = peep)
        self.pod_as_dict_exercise(inst = punk)
                        
        db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Person.pod.id,)) 
        db.execute('UPDATE pod_classes SET mtime=? WHERE id=?', (10, Punk.pod.id,)) 

        db.commit(close = True)
        
    def pod_as_dict_3(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            some_list = pod.typed.Object()
        
        class Punk(Person):
            mail = pod.typed.String(index = True)
        
        peep = [peep for peep in Person][0]
        punk = [punk for punk in Punk][0]

        self.pod_as_dict_exercise(inst = peep)
        self.pod_as_dict_exercise(inst = punk)
        
        db.commit(close = True)
        
    def pod_as_dict_4(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            some_list = pod.typed.Object()
        
        class Punk(Person):
            mail = pod.typed.String(index = True)
        
        peep = [peep for peep in Person][0]
        punk = [punk for punk in Punk][0]

        for inst in [peep, punk]:
            self <= inst.get('adad')
            self <= None
    
            self <= inst.get('adad', 10)
            self <= 10
            
            self <= inst.setdefault('foo', {'bar': 'baz'})
            self <= {'bar': 'baz'}
                
        db.commit(clear_cache = True)

        peep = [peep for peep in Person][0]
        punk = [punk for punk in Punk][0]
        
        for inst in [peep, punk]:
            self <= ('foo' in inst)
            self <= True
    
            self <= inst.foo
            self <= {'bar': 'baz'}
            
            self <= inst['foo']
            self <= {'bar': 'baz'}
            
            self <= inst.copy()
            self <= {'some_list': [10, 20, 30, 40], 'name': 'Fred', 'mail': 'hi', 'age': 20, 'foo': {'bar': 'baz'}}

            self <= inst.copy(id = True)
            self <= {'some_list': [10, 20, 30, 40], 'name': 'Fred', 'mail': 'hi', 'age': 20, 'foo': {'bar': 'baz'}, 'id': 1}
        
            inst.clear()
            
            if inst.__class__ is Person:
                self <= inst.copy()
                self <= {'some_list': None}
            else:
                self <= inst.copy()
                self <= {'mail': None, 'some_list': None}
            
            if inst.__class__ is Person:
                self <= inst.copy(id = True)
                self <= {'some_list': None, 'id': 1}
            else:
                self <= inst.copy(id = True)
                self <= {'mail': None, 'some_list': None, 'id': 1}

       
        db.commit(close = True)
        
    def pod_as_dict_5(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            some_list = pod.typed.Object()
        
        class Punk(Person):
            mail = pod.typed.String(index = True)
        
        peep = [peep for peep in Person][0]
        punk = [punk for punk in Punk][0]

        for inst in [peep, punk]:
            
            self <= ('foo' in inst)
            self <= False
            
            self <= len(inst)
            self <= (1 if inst.__class__ is Person else 2)
    
            self <= inst.copy()
            self <= ({'some_list': None} if inst.__class__ is Person else {'mail': None, 'some_list': None})

            self <= inst.copy(id = True)
            self <= ({'some_list': None, 'id': 1} if inst.__class__ is Person else {'mail': None, 'some_list': None, 'id': 1})
       
        db.commit(close = True)
        
    def pod_as_dict_create(self, cls):
        inst = cls(name = 'Fred', age = 20, some_list = [10, 20, 30, 40])
        inst['mail'] = 'hi'
        return inst
        
    def pod_as_dict_exercise(self, inst):   
             
        self <= inst['name']
        self <= 'Fred'

        self <= inst['some_list']
        self <= inst.some_list
             
        self <= ('name' in inst)
        self <= True

        self <= ('mail' in inst)
        self <= True

        self <= len(inst)
        self <= 4
        
        self <= set([key for key in inst])
        self <= set(['mail', 'some_list', 'age', 'name'])

        self <= set([key for key in inst.keys()])
        self <= set(['mail', 'some_list', 'age', 'name'])

        self <= set([key for key in inst.keys(id = True)])
        self <= set(['mail', 'some_list', 'age', 'id', 'name']) != set(['mail', 'some_list', 'age', 'name'])

        self <= set([str(value) for value in inst.values(id = False)])
        self <= set(['20', 'hi', '[10, 20, 30, 40]', 'Fred'])

        self <= set([str(value) for value in inst.values(id = True)])
        self <= set(['1', '20', 'hi', '[10, 20, 30, 40]', 'Fred']) != set(['mail', 'some_list', 'age', 'name'])

        self <= set([str(value) for value in inst.itervalues(id = True)])
        self <= set(['1', '20', 'hi', '[10, 20, 30, 40]', 'Fred']) != set(['mail', 'some_list', 'age', 'name'])
                
        self <= set([(key,str(value)) for key,value in inst.iteritems()])
        self <= set([('mail', 'hi'), ('some_list', '[10, 20, 30, 40]'), ('name', 'Fred'), ('age', '20')])

        self <= set([(key,str(value)) for key,value in inst.iteritems(id = True)])
        self <= set([('name', 'Fred'), ('mail', 'hi'), ('some_list', '[10, 20, 30, 40]'), ('id', '1'), ('age', '20')])

        self <= set([(key,str(value)) for key,value in inst.items(id = True)])
        self <= set([('name', 'Fred'), ('mail', 'hi'), ('some_list', '[10, 20, 30, 40]'), ('id', '1'), ('age', '20')])

""" CLEAN UP COVERAGE TESTS """
class Core(Tester):
    
    def test_meta_init(self):

        import pod
        
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Friend(object):
            pass

        @Tester.raises(pod.core.PodMetaError)
        def fn():
            class Person(pod.Object, Friend):
                pass
                    
        class Place(pod.Object):
            pass

        @Tester.raises(pod.core.PodMetaError)
        def fn():
            class Person(pod.Object):
                pod = 10
                id  = 20
                
        db.commit(close = True)
  
    def test_meta_object_setattr_and_execute_and_clear_and_drop(self):

        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            age = pod.typed.Int()
            
        class Punk(Person):
            pass
                
        for key in ['id', 'pod', 'store', 'where']:
            self.assertRaises(pod.core.PodMetaError, pod.core.Meta.__setattr__, Person, key, 10)

        self.assertRaises(pod.core.PodMetaError, pod.core.Meta.__setattr__, Person, 'name', pod.typed.String())

        self.assertRaises(pod.core.PodMetaError, pod.core.Meta.__setattr__, Person, 'age', 10)

        Person.hello = 20
        
        self <= Person.hello
        self <= 20
                
        peep = Person(name = 'Fred', age = 20)
        punk = Punk(name = 'Tony', age = 19)
        
        self <= Person.execute("SELECT age FROM cls_table WHERE age=?", (20,)).fetchone()[0]
        self <= 20

        self <= Person.execute("SELECT value FROM cls_table_dict WHERE key=?", ('name',)).fetchone()[0]
        self <= str('sFred')

        Person.executemany("INSERT INTO cls_table (age) VALUES (?)", ((20,), (30,), (40,)))
        Person.get_db().commit()
        self <= (Person.age == 40).get_one().age      
        self <= 40

        # Test object setattr
        
        self.assertRaises(pod.core.PodObjectError, peep.__setattr__, '__class__', object)
        self.assertRaises(pod.core.PodObjectError, punk.__setattr__, 'id', 10)

        Person.clear_all(child_classes = False)
        self <= [peep.name for peep in Person]
        self <= ['Tony']

        peep = Person(name = 'Fred', age = 20)
        db.commit()

        Person.clear_all()
        self <= [peep for peep in Person]
        self <= []

        Person.drop()
        
        import sqlite3
        self.assertRaises(sqlite3.OperationalError, Person.execute, "SELECT value FROM cls_table_dict WHERE key=?", ('name',))
        self.assertRaises(sqlite3.OperationalError, Punk.execute, "SELECT value FROM cls_table_dict WHERE key=?", ('name',))
        
        db.commit(close = True)
  
    def test_meta_typed_conflict(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            age = pod.typed.Int()
            
        @Tester.raises(pod.core.PodMetaError)
        def fn():
            class Junk(Person):
                age = pod.typed.Int()
        
        db.commit(close = True)
  
    def test_pod_pickle(self):
        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            age = pod.typed.Int()
        
        import cPickle
        self.assertRaises(pod.core.PodMetaError, cPickle.dumps, Person.pod)

        db.commit(close = True)
  
    def test_migrate(self):

        self.migrate_0()
        self.clear_mod_imports()
        self.migrate_1()
        self.clear_mod_imports()
        self.migrate_2()
        self.clear_mod_imports()
        self.migrate_3()
        self.clear_mod_imports()
    
    def migrate_0(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            pass
        
        class Punk(Person):
            pass
                
        peep  = Person(name = 'Fred')
        punk  = Punk(name = 'Tony')
        
        db.commit(close = True)

    def migrate_1(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class Person(pod.Object):
            pass

        class NewPerson(object):
            pass
        
        self.assertRaises(pod.core.PodMetaError, Person.migrate, NewPerson)
        db.commit(close = True)

    def migrate_2(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)

        class Person(pod.Object):
            pass
        
        class Punk(Person):
            pass
        
        class NewPerson(pod.Object):
            pass

        self <= [peep.name for peep in pod.Query(where=Person)]
        self <= ['Fred', 'Tony']

        self <= [peep.name for peep in pod.Query(where=Person, child_classes = False)]
        self <= ['Fred']

        Person.migrate(new_cls = NewPerson)
        db.commit(close = True)

    def migrate_3(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = False, very_chatty = False)
        
        class NewPerson(pod.Object):
            pass
        
        class Punk(NewPerson):
            pass
        
        self <= (NewPerson.where.name == 'Fred').get_one().name
        self <= 'Fred'
        
        self <= [peep.name for peep in NewPerson]
        self <= ['Fred', 'Tony']
        
        db.commit(close = True)

    def test_table_name(self):

        import pod
        
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            age = pod.typed.Int()
    
        class Place(pod.Object):
            POD_TABLE_NAME = True

        class Thing(pod.Object):
            POD_TABLE_NAME = 'SOMETHING'
            
        self <= Person.pod.table
        self <= '__main___Person'
            
        self <= Place.pod.table
        self <= 'Place' 
        
        self <= Thing.pod.table
        self <= 'SOMETHING'
            
        db.commit(close = True)

    def test_drop_and_delete_forever_nochat(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            age = pod.typed.Int()
            
        class Punk(Person):
            pass
                 
        peep = Person(name = 'Fred', age = 20)
        punk = Punk(name = 'Tony', age = 19)
                
        Person.age.drop()
        
        self <= getattr(peep, 'age', None)
        self <= None
        
        self <= getattr(punk, 'age', None)
        self <= None
        
        self <= peep.copy()
        self <= {'name': 'Fred'}
        
        db.commit(close = True)

    def test_drop_and_delete_forever_chat(self):
        
        import pod
        db = pod.Db(file = self.filename, remove = True, chatty = True)
        
        class Person(pod.Object):
            age = pod.typed.Int()
            
        class Punk(Person):
            pass
                 
        peep = Person(name = 'Fred', age = 20)
        punk = Punk(name = 'Tony', age = 19)
                
        Person.age.drop()
        
        self <= getattr(peep, 'age', None)
        self <= None
        
        self <= getattr(punk, 'age', None)
        self <= None
        
        self <= peep.copy()
        self <= {'name': 'Fred'}
        
        db.commit(close = True)

    def test_object_del_attr(self):
        
        import pod
        db = pod.Db(file = self.filename, dynamic_index = True, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            age = pod.typed.Int(index = True)
            
        peep = Person(name = 'Fred', age = 20, weight = 170.2)
        
        del peep.name
        del peep.age
        
        self <= peep.copy()
        self <= {'age': None, 'weight': 170.19999999999999}
        
        db.commit(clear_cache = True)
        
        self <= [peep for peep in Person.where.name]
        self <= []
        
        db.commit(close = True)

    def test_inst_update_dict(self):
        
        import pod
        
        db = pod.Db(file = self.filename, dynamic_index = True, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            age = pod.typed.Int(index = True)
            foo = pod.typed.String(index = False)
            
        peep = Person(name = 'Fred', age = 20, weight = 170.2)
        
        peep.update( {'age':  30, 'foo': 'bar', 'black': 'white'} )
        
        peep.set_many(baz = '10', some_other_attr = '20')
        
        self <= set(peep.keys())
        self <= set(['some_other_attr', 'name', 'weight', 'age', 'baz', 'black', 'foo'])

        self <= set(peep.values())
        self <= set(['bar', '20', '10', 'Fred', 170.19999999999999, 'white', 30])
        
        db.commit(close = True)
              
    def test_booleans(self):

        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Person(pod.Object):
            age      = pod.typed.Int()
            is_alive = pod.typed.Object() 
            
        class Skater(Person):
            weight = pod.typed.Float()

        class Email(pod.Object):
            title = pod.typed.String()
            
        p0 = Person(age = 32, weight = 204, random = '10', is_alive = True, attr = False)
        p1 = Person(age = 16, weight = 134, random = '5',  is_alive = False, attr = None)

        s0 = Skater(age = 10, weight = 204, random = '10', is_alive = None, attr = True)
        s1 = Skater(age = 20, weight = 134, random = '5',  is_alive = False, attr = None)

        e0 = Email(title = 'HI')
        e1 = Email(title = 'BYE')
        
        db.commit()
        
        p0.is_alive = True
        p1.is_alive = False
        
        db.commit()

        self <= [peep.age for peep in (Person.is_alive == True) | (Person.where.is_alive == False)]
        self <= [32, 16, 20]

        self <= [peep.age for peep in (Person.where.attr == None) | (Person.where.attr == True)]
        self <= [16, 10, 20]

        db.commit(clear_cache = True)
        
        self <= [peep.is_alive for peep in (Person.is_alive == True) | (Person.where.is_alive == False)]
        self <= [True, False, False]
        
        self <= [peep.attr for peep in (Person.is_alive == True) | (Person.where.is_alive == False)]
        self <= [False, None, None]
        
        db.commit(close = True)
    
    def test_no_database(self):
        
        import pod 
        
        class Person(pod.Object):
            age      = pod.typed.Int()
            is_alive = pod.typed.Object() 

        class Punk(Person):
            pass
        
        @Tester.raises(pod.core.PodNoDatabaseError)
        def fn():
            Person.get_count()

        @Tester.raises(pod.core.PodNoDatabaseError)
        def fn():
            Punk.pod.some_attr = 'Foo'

        @Tester.raises(pod.core.PodNoDatabaseError)
        def fn():
            for punk in Punk:
                punk

""" CLEAN UP COVERAGE TESTS """
class Db(Tester):
    
    def test_two_globals(self):

        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Friend(object):
            pass
                
        @Tester.raises(pod.db.PodDbError)
        def fn():
            pod.Db(file = ':memory:', remove = True, very_chatty = False)
                    
        db.commit(close = True)
  
    def test_attach_unsupported_type(self):
        
        import pod
        
        class Friend(object):
            pass
        
        @Tester.raises(pod.db.PodDbError)
        def fn():
            pod.Db(file = ':memory:', remove = True, attach = Friend, very_chatty = False)
                    
    def test_attach_then_global(self):

        import pod
        
        class Friend(pod.Object):
            pass
        
        db = pod.Db(file = self.filename, remove = True, attach = Friend, very_chatty = False)
        
        @Tester.raises(pod.db.PodDbError)
        def fn():
            pod.Db(file = ':memory:', remove = True, very_chatty = False)
                    
        db.commit(close = True)

    def test_global_then_attach(self):

        import pod

        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        class Friend(pod.Object):
            pass
        
        @Tester.raises(pod.db.PodDbError)
        def fn():
            pod.Db(file = ':memory:', attach = Friend, remove = True, very_chatty = False)
                    
        db.commit(close = True)
        
    def test_no_file_then_file_and_connect(self):

        import pod
        
        db = pod.Db(file = self.filename, very_chatty = True) 
        
        class Friend(pod.Object):
            pass
        
        Friend()
        Friend()
        Friend()
 
        self <= len([f for f in Friend])
        self <= 3

        db.commit(close = True)
        
    def test_store(self):

        import pod
        db = pod.Db(file = self.filename, remove = True, very_chatty = False)
        
        db.store.fred = 'Fred'
        
        self <= db.store.fred
        self <= db.store['fred']
        
        @Tester.raises(pod.db.PodStoreError)
        def fn():
            db.store.no_key
                    
        db.commit(close = True)

    def test_clear_db(self):
        self.db_clear_db_0()
        self.clear_mod_imports()
        self.db_clear_db_1()
        self.clear_mod_imports()
        self.db_clear_db_0()
        self.clear_mod_imports()
        self.db_clear_db_2()
        self.clear_mod_imports()
    
    def db_clear_db_0(self):
        
        import pod

        class Person(pod.Object):
            age = pod.typed.Int()
            
        class Punk(pod.Object):
            pass

        db = pod.Db(file = self.filename, remove = True, dynamic_index = True, very_chatty = False)

        Person()
        Person()
        Person()
        Punk()
            
        self <= db.cursor.get_table_list()
        self <= ['pod_classes', 'sqlite_sequence', '__main___Person', '__main___Person_kvdict', '__main___Punk', '__main___Punk_kvdict']
        
        db.commit(close = True)

    def db_clear_db_1(self):
        
        import pod

        class Person(pod.Object):
            age = pod.typed.Int()
            
        class Punk(pod.Object):
            pass

        db = pod.Db(file = self.filename, remove = False, dynamic_index = True, chatty = True)
            
        self <= db.cursor.get_table_list()
        self <= ['pod_classes', 'sqlite_sequence', '__main___Person', '__main___Person_kvdict', '__main___Punk', '__main___Punk_kvdict']
        
        db.clear()

        self <= db.cursor.get_table_list()
        self <= []
        
        db.commit(close = True)

    def db_clear_db_2(self):
        
        import pod

        db = pod.Db(file = self.filename, remove = False, clear = True, dynamic_index = True, very_chatty = False)

        self <= db.cursor.get_table_list()
        self <= ['pod_classes', 'sqlite_sequence'] 
        
        class Person(pod.Object):
            age = pod.typed.Int()
            
        class Punk(pod.Object):
            pass

        Person()
        Person()
        Person()
        Punk()
            
        self <= db.cursor.get_table_list(report_collections = False)
        self <= ['pod_classes', 'sqlite_sequence', '__main___Person', '__main___Person_kvdict', '__main___Punk', '__main___Punk_kvdict']

        db.vacuum()

        cursor = db.get_new_cursor()
        
        self <= [str(table[0]) for table in cursor.execute(query = 'select name from sqlite_master where type = ?', args = ('table',))]
        self <= ['pod_classes', 'sqlite_sequence', '__main___Person', '__main___Person_kvdict', '__main___Punk', '__main___Punk_kvdict']

        cursor.executemany('INSERT INTO __main___Person (age) VALUES (?)', ((10,), (20,), (30,)))
        
        self <= set([peep.age for peep in Person])
        self <= set([None, None, None, 10, 20, 30])
        
        db.commit(close = True)

    def test_cancel(self):
        self.db_cancel_0()
        self.clear_mod_imports()
        self.db_cancel_1()
        self.clear_mod_imports()
        self.db_cancel_2()
        self.clear_mod_imports()
        self.db_cancel_3()
        self.clear_mod_imports()
    
    def db_cancel_0(self):
        
        import pod

        class Person(pod.Object):
            age = pod.typed.Int()
            
        class Punk(pod.Object):
            pass

        db = pod.Db(file = self.filename, remove = True, dynamic_index = True, very_chatty = True)

        self <= [p for p in Person]
        self <= []

        Person(name = 'Fred')
        Punk(name = 'Greg')

        db.rollback(close = True)
        
    def db_cancel_1(self):
        
        import pod

        class Person(pod.Object):
            age = pod.typed.Int()
            
        class Punk(pod.Object):
            pass

        db = pod.Db(file = self.filename, remove = False, dynamic_index = True, very_chatty = True)

        self <= [p.name for p in Person]
        self <= []
        
        db.commit(close = True)
        
    def db_cancel_2(self):
        
        import pod

        class Person(pod.Object):
            age = pod.typed.Int()
            
        class Punk(pod.Object):
            pass

        db = pod.Db(file = self.filename, remove = True, dynamic_index = True, very_chatty = False)

        self <= [p for p in Person]
        self <= []

        Person(name = 'Fred')
        Punk(name = 'Greg')

        db.rollback(close = True)
        
    def db_cancel_3(self):
        
        import pod

        class Person(pod.Object):
            age = pod.typed.Int()
            
        class Punk(pod.Object):
            pass

        db = pod.Db(file = self.filename, remove = False, dynamic_index = True, very_chatty = False)

        self <= [p.name for p in Person]
        self <= []
        
        db.commit(close = True)

class Attach(Tester):

    def test_attach_hierarchy(self):
        
        import pod

        class W(pod.Object):
            age = pod.typed.Int

        class X(W):
            pass

        class Y(X):
            pass

        class Z(Y):
            pass

        db = pod.Db(file = self.filename, remove = True, attach = [X, Z])
        
        @Tester.raises(pod.core.PodNoDatabaseError)
        def fn():
            W()
            
        Y()  # This should be okay . . . 
            
        self <= X.pod.db
        self <= db
        
        self <= Z.pod.db
        self <= db
        
        self <= object.__getattribute__(Y.pod, 'db')
        self <= db

        self <= object.__getattribute__(Y.pod, 'id')
        self <= 2
        
        X(name = 'X0')
        X(name = 'X1')
        Z(name = 'Z0')
        Z(name = 'Z1')
        
        self <= len([a for a in X])
        self <= 5

        self <= len([a.name for a in X.where.name])
        self <= 4

        self <= len([a for a in Y])
        self <= 3
                    
        db.rollback(close = True)
      
    def test_attach_two_db_error(self):
        self.attach_two_db_error()
        self.clear_mod_imports()
    
    def attach_two_db_error(self):
        
        import pod

        class W(pod.Object):
            age = pod.typed.Int

        class X(W):
            pass

        db0 = pod.Db(file = self.filename, remove = True, attach = [W])
        
        @Tester.raises(pod.core.PodMetaError)
        def fn():
            db1 = pod.Db(file = ':memory:', remove = True, attach = [X])
        
        w0 = W()
        w1 = W()

        db0.commit(close = True)
        

        
    def test_two_dbs_error(self):
        
        import pod

        class W(pod.Object):
            age = pod.typed.Int

        class X(pod.Object):
            pass

        import pod.list as list_0
        import pod.list as list_1
        
        db0 = pod.Db(file = self.filename, remove = True, attach = [X, list_0])
        @Tester.raises(pod.core.PodMetaError)
        def fn():
            db1 = pod.Db(file = ':memory:', remove = True, attach = [W, list_1])

    def test_double_attach(self):
        
        import pod

        class W(pod.Object):
            age = pod.typed.Int

        db = pod.Db(file = self.filename, remove = True, attach = [W])
        db.attach(obj = W)
        db.attach(obj = W)
        db.attach(obj = W)
        
                    
        db.rollback(close = True)

                        
"""
The tests . . . 
"""
Tester.reporter_start(use_figleaf = True)    
Tester.run_test(Basics)
Tester.run_test(Extended)
Tester.run_test(NoSaveUndefinedDeleted)
Tester.run_test(AltSchema)
Tester.run_test(Collections)
Tester.run_test(PodAsDict)
Tester.run_test(Timers)
Tester.run_test(Raw)
Tester.run_test(Core)
Tester.run_test(Db)
Tester.run_test(Attach)
Tester.reporter_stop()

